package com.yfbao.horizon.schedule.tk.aspect;


import com.yfbao.horizon.schedule.tk.common.LocalCacheUtil;
import com.yfbao.horizon.schedule.tk.annotation.CompositeCacheable;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.core.annotation.Order;
import org.springframework.expression.Expression;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;


@Aspect
@Slf4j
@Component
@Order(-1)
public class MyCacheAspect {

    @Autowired
    private LocalCacheUtil cacheUtil;
//    @Autowired
//    private LocalCacheSerializerUtil cacheUtil;

    /**
     * 环绕处理
     * @param joinPoint
     * @return
     * @throws Throwable
     */
    @Around("@annotation(cacheable) && execution(* *(..))")
    public Object around(ProceedingJoinPoint joinPoint, Cacheable cacheable) throws Throwable {
//        log.info("begin do find data from  localCahe");
        if(!cacheUtil.cacheEnable()){
            //没有开启缓存代理
            return joinPoint.proceed();
        }
        String[] cacheNames =new String[]{};
        String cacheKey = "";
        try{
            cacheNames = cacheable.cacheNames();
            if(cacheNames ==null || cacheNames.length==0){
                CacheConfig annotation = joinPoint.getTarget().getClass().getAnnotation(CacheConfig.class);
                cacheNames = annotation.cacheNames();
            }
            cacheKey = cacheable.key();
            Object[] args = joinPoint.getArgs();
            if(StringUtils.isBlank(cacheKey)){
                for (Object arg : args) {
                    if(StringUtils.isBlank(cacheKey)){
                        cacheKey=arg.toString();
                    }else {
                        cacheKey=cacheKey+"_"+arg.toString();
                    }
                }
            }else {
                // 获取方法签名
                Signature signature = joinPoint.getSignature();
                // 检查方法签名是否是 MethodSignature 类型
                if (signature instanceof MethodSignature) {
                    MethodSignature methodSignature = (MethodSignature) signature;
                    // 获取方法
//                    Method method = methodSignature.getMethod();
//                    // 获取方法参数类型
//                    Class<?>[] parameterTypes = methodSignature.getParameterTypes();

                    // 获取方法参数名称
                    String[] parameterNames = methodSignature.getParameterNames();
                    // 使用 SpelExpressionParser 解析 SpEL 表达式
                    SpelExpressionParser spelExpressionParser = new SpelExpressionParser();
                    Expression expression = spelExpressionParser.parseExpression(cacheKey);
                    StandardEvaluationContext evaluationContext = new StandardEvaluationContext();
                    for (int i = 0; i < args.length; i++) {
                        evaluationContext.setVariable(parameterNames[i],args[i]);
                    }
                    // 计算 SpEL 表达式的值
                    cacheKey = expression.getValue(evaluationContext, String.class);
                }
            }
            for (String cacheName : cacheNames) {
                Object o = cacheUtil.getCache(cacheName,cacheKey);
                if(o!=null){
                    log.info("find data from localCahe:"+cacheUtil.getCompleteCacheKey(cacheName,cacheKey));
                    return o;
                }
            }
        }catch (Exception e){
            log.error("do proxy cache error",e);
        }

        log.info("find data  proceed");
        Object proceed = joinPoint.proceed();
        if(cacheNames.length==0){
            return proceed;
        }
        for (String cacheName : cacheNames) {
            if(StringUtils.isBlank(cacheKey)){
                break;
            }
            cacheUtil.addCache(cacheName,cacheKey,proceed);
        }
        return proceed;

    }


    /**
     * 环绕处理
     * @param joinPoint
     * @return
     * @throws Throwable
     */
    @Around("@annotation(cacheEvict) && execution(* *(..))")
    public Object around(ProceedingJoinPoint joinPoint, CacheEvict cacheEvict) throws Throwable {
        log.info("begin do evict data from  localCahe");
        if(!cacheUtil.cacheEnable()){
            //没有开启缓存代理
            return joinPoint.proceed();
        }
        String[] cacheNames =new String[]{};
        String cacheKey = "";
        try{
            cacheNames = cacheEvict.cacheNames();
            if(cacheNames ==null || cacheNames.length==0){
                CacheConfig annotation = joinPoint.getTarget().getClass().getAnnotation(CacheConfig.class);
                cacheNames = annotation.cacheNames();
            }
            cacheKey = cacheEvict.key();
            Object[] args = joinPoint.getArgs();
            if(StringUtils.isBlank(cacheKey)){
                for (Object arg : args) {
                    if(StringUtils.isBlank(cacheKey)){
                        cacheKey=arg.toString();
                    }else {
                        cacheKey=cacheKey+"_"+arg.toString();
                    }
                }
            }else {
                // 获取方法签名
                Signature signature = joinPoint.getSignature();
                // 检查方法签名是否是 MethodSignature 类型
                if (signature instanceof MethodSignature) {
                    MethodSignature methodSignature = (MethodSignature) signature;
                    // 获取方法
//                    Method method = methodSignature.getMethod();
                    // 获取方法参数类型
//                    Class<?>[] parameterTypes = methodSignature.getParameterTypes();
                    // 获取方法参数名称
                    String[] parameterNames = methodSignature.getParameterNames();
                    // 使用 SpelExpressionParser 解析 SpEL 表达式
                    SpelExpressionParser spelExpressionParser = new SpelExpressionParser();
                    Expression expression = spelExpressionParser.parseExpression(cacheKey);
                    StandardEvaluationContext evaluationContext = new StandardEvaluationContext();
                    for (int i = 0; i < args.length; i++) {
                        evaluationContext.setVariable(parameterNames[i],args[i]);
                    }
                    // 计算 SpEL 表达式的值
                    cacheKey = expression.getValue(evaluationContext, String.class);
                }
            }
            for (String cacheName : cacheNames) {
                Object o = cacheUtil.getCache(cacheName,cacheKey);
                if(o!=null){
                    log.info("find data from localCahe:"+cacheUtil.getCompleteCacheKey(cacheName,cacheKey));
                    return o;
                }
            }
        }catch (Exception e){
            log.error("do proxy cache error",e);
        }
        for (String cacheName : cacheNames) {
            cacheUtil.cacheEvict(cacheName,cacheKey);
        }
        log.info("evict data  proceed");
        Object proceed = joinPoint.proceed();

        return proceed;

    }

    /**
     * 环绕处理
     * @param joinPoint
     * @return
     * @throws Throwable
     */
    @Around("@annotation(cacheable) && execution(* *(..))")
    @Order(1)
    public Object around2(ProceedingJoinPoint joinPoint, CompositeCacheable cacheable) throws Throwable {
        log.info("find data from  CompositeCacheable localCahe");
        String[] strings = cacheable.cacheNames();
        String key = cacheable.key();
        Object[] args = joinPoint.getArgs();
        if(key==null){
            key="";
            for (Object arg : args) {
                key=key+"_"+arg.toString();
            }
        }
        for (String string : strings) {
            String cacheKey = string+":"+key;
            Object o = LocalCacheUtil.localCache.getIfPresent(cacheKey);
            if(o!=null){
                log.info("find data from localCahe");
                return o;
            }
        }
        Object proceed = joinPoint.proceed();

        for (String string : strings) {
            String cacheKey = string+":"+key;
            LocalCacheUtil.localCache.put(cacheKey,proceed);
        }
        return proceed;

    }

}
